package com.zhupanlin.sqlSession;

import com.zhupanlin.pojo.Configuration;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;

/**
 * 描述信息：
 *
 * @author zhupanlin
 * @version 1.0
 * @date 2025/2/8 17:59
 */
public class DefaultSqlSession implements SqlSession{
    
    private Configuration configuration;

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public <E> List<E> selectList(String statementId, Object... params) throws Exception {
        
        // 将要完成对 simpleExecutor 里的 query 方法的调用 
        SimpleExecutor simpleExecutor = new SimpleExecutor();
        List<E> result = simpleExecutor.query(configuration, configuration.getMappedStatementMap().get(statementId), params);

        return result;
    }

    @Override
    public <T> T selectOne(String statementId, Object... params) throws Exception {
        List<Object> objects = selectList(statementId, params);
        if (objects.size() == 1) {
            return (T) objects.get(0);
        }
        
        throw new RuntimeException("Expected one result (or null) to be returned by selectOne(), but found: " + objects.size());
    }

    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        
        // 使用 JDK 动态代理来为 Dao 接口生产代理对象，并返回
        Object proxyInstance = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{mapperClass}, (proxy, method, args) -> {
            // 底层都还是去执行 JDBC 代码
            // 得到参数 statementId
            // 接口全限定名
            String className = method.getDeclaringClass().getName();
            // 方法名
            String methodName = method.getName();
            
            String statementId = className + "." + methodName;
            // 获取被调用方法的返回值类型
            Type genericReturnType = method.getGenericReturnType();
            
            // 判断返回值类型是否有泛型
            if (genericReturnType instanceof ParameterizedType) {
                List<Object> objects = selectList(statementId, args);
                return objects;
            }
            return selectOne(statementId, args);
        });

        return (T) proxyInstance;
    }
}